'''
####
Date : 2025-1-9
Notes: UI界面显示
此脚本主要功能:
1. 获取视觉图像

####
'''

# -*- coding: utf-8 -*-
import cv2
import os
import logging
import time
import queue
import threading
import imutils
import numpy as np
# from paddleocr import PaddleOCR
# from cnocr import CnOcr

import struct
import base64
import socket
import json
import platform

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


current_dir = os.path.dirname(os.path.abspath(__file__))
res_file_path = os.path.join(current_dir, "..", "img/res", "result.png")

kernel_3 = np.ones((3, 3), np.uint8)  # 3x3的卷积核
kernel_5 = np.ones((5, 5), np.uint8)  
kernel_7 = np.ones((7, 7), np.uint8)

draw_color = (0,255,0)

Lower_red = np.array([0, 97, 113])  
Upper_red = np.array([180, 255, 255])
draw_red = draw_color
red = [Lower_red, Upper_red, 'red',draw_red]

Lower_blue = np.array([106, 219, 142])
Upper_blue = np.array([111, 255, 225])
draw_blue =draw_color
blue = [Lower_blue, Upper_blue, 'blue',draw_blue]

Lower_green = np.array([39, 60, 133])
Upper_green = np.array([96, 255, 255])
draw_green =draw_color
green = [Lower_green, Upper_green, 'green',draw_green]

Lower_yellow = np.array([21, 80, 200])   
Upper_yellow = np.array([37, 99, 255])
draw_yellow = draw_color
yellow = [Lower_yellow, Upper_yellow, 'yellow',draw_yellow]

Lower_black = np.array([100, 118, 37])
Upper_black = np.array([107, 193, 69])
draw_black = (0,0,0)
black = [Lower_black, Upper_black, 'black',draw_black]

color_list = [red,blue,green,yellow]

dir=os.path.dirname(__file__)
import sys
sys.path.append(dir)

### version 1.0.2
class Camera_Thread(threading.Thread):
    
    def __init__(self, label):
        super().__init__()
        self.stop = True
        self.Is_exit = False
        self.cap = None
        ## 存放原始图像帧
        self.raw_frame_queue = queue.Queue(maxsize=1)  
        ## 存放处理后的图像帧
        self.processed_frame_queue = queue.Queue(maxsize=1)  
        self.label = label
        # self.label_res = label_res
        ## 控制是否进行二维码检测的标志
        self.detect_qr_code = 0  
        self.resultInfo = None
        ## 视觉检测参数配置
        # self.parameterInfo = list()
        self.parameterInfo = []
        self.seen_items = set()  
        
        # 存放检测结果和中心点坐标
        self.result_message = []

        self.virtualCam = IOACamera()

    def exit(self):
        self.stop = True
        self.Is_exit = True
        if self.cap is not None:
            self.cap.release()

    def add_parameter_info(self, msg):
            # 如果msg不在seen_items中，则添加到parameterInfo和seen_items
            if msg not in self.seen_items:
                self.parameterInfo.append(msg)
                self.seen_items.add(msg)

    def save_img(self,image):
        try:
            cv2.imwrite(res_file_path, image)
        except Exception as e:
            print(e)

    ### 二维码识别--调用微信开源库
    def QR_test(self, img):
        try:
            detect_prototxt = os.path.join(dir, 'detect.prototxt')  
            detect_caffemodel = os.path.join(dir, 'detect.caffemodel')  
            sr_prototxt = os.path.join(dir, 'sr.prototxt')  
            sr_caffemodel = os.path.join(dir, 'sr.caffemodel')
        
            # 创建 WeChatQRCode 对象  
            detect_obj = cv2.wechat_qrcode_WeChatQRCode(  
                detect_prototxt,  
                detect_caffemodel,  
                sr_prototxt,  
                sr_caffemodel  
            )   
            res, points = detect_obj.detectAndDecode(img)
            print(f"QR_test res is {res}")
            
            # if res:
            #     self.parameterInfo.append(f"检测识别结果: {res}")
            # else:
            #     self.parameterInfo.append("未检测到二维码或无法解码")
            
            # 清空之前检测的信息
            self.result_message = []
            
            if points is not None:
                for idx, qr_points in enumerate(points):
                    ## 绘制中心点和边界线
                    x0, y0 = qr_points[0]
                    x1, y1 = qr_points[2]
                    center_x = (x0 + x1) / 2
                    center_y = (y0 + y1) / 2
                    center_point = (int(center_x), int(center_y))
                    cv2.circle(img, center_point, 5, (0, 255, 0), -1)

                    color = (0, 0, 255)
                    thick = 3
                    for p in [(0, 1), (1, 2), (2, 3), (3, 0)]:
                        start = tuple(map(int, qr_points[p[0]]))
                        end = tuple(map(int, qr_points[p[1]]))
                        cv2.line(img, start, end, color, thick)

                    ## 显示解码结果
                    qr_code_text = res[idx]
                    cv2.putText(img, qr_code_text[:1], (int(x0), int(y0) - 10), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 125), 2)
                    
                    ## 添加打印日志
                    self.resultInfo = "QR: " + qr_code_text
                    
                    # 存放检测结果和中心点坐标
                    result = []
                    result.append(qr_code_text)
                    result.append(center_point)
                    self.result_message.append(result)
                    
                    print(f"self.resultInfo: {self.resultInfo}")
                    print(f"self.result_message: {self.result_message}")
                    
                    detect_model_info = "定义存储的Caffe模型: \n detect.prototxt, detect.caffemodel\n"
                    sr_model_info = "超分辨率模型: \n sr.prototxt, sr.caffemodel\n"

                    # 尝试添加信息，如果不存在则添加
                    self.add_parameter_info(detect_model_info)
                    self.add_parameter_info(sr_model_info)
            self.save_img(img)
            return img
        
        except Exception as e:
            print(f"Error in QR_test: {e}")
            return img

    ### OCR识别--百度飞桨OCRv4 
    def OCR_test(self, img):
        try:
            ## 可以根据需要更改语言参数
            self.ocr = PaddleOCR(use_angle_cls=True, lang='ch')

            # 执行文字检测和识别
            result = self.ocr.ocr(img, cls=True)
            print(f"ocr result is {result}")
            
            # 清空之前检测的信息
            self.result_message = []
            
            # 如果有识别结果，则处理并显示
            if result is not None:
                for res in result:
                    for line in res:
                        print(f"line {line}")
                        boxes = line[0]  # 文本框坐标
                        txts, scores = line[1]


                        # pts = [tuple(map(int, point)) for point in boxes]
                        # cv2.polylines(img, [np.array(pts)], isClosed=True, color=(0, 255, 0), thickness=2)
                        color = (0, 0, 255)
                        thick = 3
                        for p in [(0, 1), (1, 2), (2, 3), (3, 0)]:
                            start = tuple(map(int, boxes[p[0]]))
                            end = tuple(map(int, boxes[p[1]]))
                            cv2.line(img, start, end, color, thick)
                        
                        # 在文本框上方显示识别出的文本
                        text_x, text_y = int(boxes[0][0]), int(boxes[1][1]) - 10
                        # print(text_x, text_y)
                        # print(f"文本Text: {txts}, Confidence: {scores}")
                        
                        cv2.putText(img, txts, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
                        # cv2.putText(img, qr_code_text[:1], (int(x0), int(y0) - 10), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 125), 2)

                        # font = cv2.FONT_HERSHEY_SIMPLEX
                        # cv2.putText(img, txts, (int(boxes[0][0]), int(boxes[0][1])-10), font, 1, (0, 0, 255), 2, cv2.LINE_AA)
                        
                        # 更新结果信息
                        self.resultInfo = "文本: " + txts
                        
                        # 存放检测结果和中心点坐标
                        x0, y0 = boxes[0]
                        x1, y1 = boxes[2]
                        center_point = (int((x0 + x1) / 2), int((y0 + y1) / 2))
                        result = []
                        result.append(txts)
                        result.append(center_point)
                        self.result_message.append(result)
                        
                        print(f"self.resultInfo: {self.resultInfo}")
                        print(f"self.result_message: {self.result_message}")
                
                        # 添加模型信息（这里假设你想要记录一些关于模型的信息）
                        model_info = "使用的PaddleOCR模型: \n ch_PP-OCRv4_det_infer, ch_PP-OCRv4_rec_infer \n"
                        self.add_parameter_info(model_info)
            self.save_img(img)
            return img
            
        except Exception as e:
            print(f"Error in OCR_test: {e}")
            return img

    # def OCR_test(self,img):
    #     try:
    #         ### 所有参数都使用默认值即可，查看可直接进入函数
    #         ocr = CnOcr()  
    #         ress = ocr.ocr(img)
            
    #         # 清空之前检测的信息
    #         self.result_message = []

    #         if ress:
    #             for res in ress:
    #                 text = res['text']  
    #                 position = res['position']   
    #                 ### 计算矩形中心  
    #                 x1, y1 = int(position[0][0]), int(position[0][1])  
    #                 x2, y2 = int(position[2][0]), int(position[2][1])  
    #                 center_x = int((x1 + x2) / 2)  
    #                 center_y = int((y1 + y2) / 2)
    #                 center_point = (int(center_x), int(center_y))
                    
    #                 ### 检测目标绘制矩形框 
    #                 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
    #                 ### 检测目标中心画圆
    #                 cv2.circle(img, (center_x, center_y), 5, (0, 0, 255), -1)

    #                 if text == "A" or text== "4" or text == "^" or text == "Y":
    #                     text == "A"
    #                 elif text == "B" or text == "8":
    #                     text == "B"
    #                 elif text == "C":
    #                     text == "C"
    #                 elif text == "D" or text == "0" or text == "O" or text == "o":
    #                     text == "D"
    #                 ### 图形显示识别到的字符                       
    #                 # print("识别到的字符为：%s" %text)
    #                 # 更新结果信息
    #                 self.resultInfo = "文本: " + text
    #                 ### 图形显示识别到的字符
    #                 cv2.putText(img, text, (int(x1), int(y1)- 10), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 2)
    #                 result = []
    #                 result.append(text)
    #                 result.append(center_point)
    #                 self.result_message.append(result)
                    
    #                 print(f"self.resultInfo: {self.resultInfo}")
    #                 print(f"self.result_message: {self.result_message}")
    #         self.save_img(img)
    #         return img

    #     except Exception as e:
    #         logger.error(e,exc_info=True)

    ### 形状识别--OpenCV
    def shape_test(self, frame):
        max_area = 200000
        min_area = 1800
        
        # 定义一个形状参数用于存储结果
        shape_Num = 0
        
        imgGrey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        _, thresh = cv2.threshold(imgGrey, 80, 255, cv2.THRESH_BINARY)
        
        # cv2.imwrite("./img/res/thresh.png", thresh)
        inverted_binary = cv2.bitwise_not(thresh)
        # cv2.imwrite("./img/res/inverted_binary.png", inverted_binary)
        contours, _ = cv2.findContours(inverted_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
        
        # 清空之前检测的信息
        self.result_message = []
        
        for contour in contours:
            contour_area = cv2.contourArea(contour)
            if contour_area > min_area and contour_area < max_area:
                approx = cv2.approxPolyDP(contour, 0.01* cv2.arcLength(contour, True), True)
                cv2.drawContours(frame, [approx], 0, (0,0,255), 5)
                x = approx.ravel()[0]
                y = approx.ravel()[1] - 5
                if len(approx) == 3:
                    cv2.putText(frame, "Triangle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                    self.resultInfo = f"形状 三角形"
                    shape_Num = 1

                elif len(approx) == 4:
                    x1, y1, w, h = cv2.boundingRect(approx)
                    aspectRatio = float(w)/h
                    
                    if aspectRatio >= 0.95 and aspectRatio <= 1.05:
                        cv2.putText(frame, "Square", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                        self.resultInfo = f"形状 正方形"
                        shape_Num = 2

                    else:
                        cv2.putText(frame, "Rectangle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                        self.resultInfo = f"形状 矩形"
                        shape_Num = 3
                
                elif len(approx) == 5:
                    cv2.putText(frame, "Pentagon", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                    self.resultInfo = f"形状 五边形"
                    shape_Num = 4

                elif len(approx) == 6:
                    cv2.putText(frame, "Hexagon", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                    self.resultInfo = f"形状 六边形"
                    shape_Num = 5
                
                elif len(approx) == 10:
                    cv2.putText(frame, "Star", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                    self.resultInfo = f"形状 五角星"
                    shape_Num = 6
                
                else:
                    cv2.putText(frame, "Circle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0,0,255))
                    self.resultInfo = f"形状 圆形"
                    shape_Num = 7
                
                # 形状识别暂不支持入库操作
                # # 计算轮廓的质心  
                # M = cv2.moments(contour)  
                # if M['m00'] != 0:  # 防止除以零  
                #     cx = int(M['m10'] / M['m00'])  
                #     cy = int(M['m01'] / M['m00'])
                # # 存放检测结果和中心点坐标
                # center_point = (cx, cy)
                # result = []
                # result.append(shape_Num)
                # result.append(center_point)
                # self.result_message.append(result)
                print(f"self.resultInfo: {self.resultInfo}")
                # print(f"self.result_message: {self.result_message}")
                
                # 添加参数信息
                model_info = f" 二值化阈值为 [80, 255], 检测区域面积[1800, 200000]\n"
                self.add_parameter_info(model_info)
        
        cv2.imwrite(res_file_path, frame) # 移到循环外，确保所有检测结果都绘制在图像上后再保存
        return frame

    ### 检测识别-- 颜色识别
    def color_test(self, image):
        w = 70
        h = 70
        ##### 把BGR图像转换为HSV格式
        HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
        kernel = np.ones((3,3), np.uint8) # 定义卷积核大小
        
        # 清空之前检测的信息
        self.result_message = []
        
        for color in color_list:
            mask_red = cv2.inRange(HSV, color[0], color[1])
            
            ### 形态学去噪
            mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel) # 开运算
            
            ### 二值化
            ret, binary = cv2.threshold(mask_red, 0, 255, cv2.THRESH_BINARY)
            cv2.imwrite("./img/res/binary.png", binary)
            cnts = cv2.findContours(binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
            cnts = imutils.grab_contours(cnts)

            if cnts:
                for c in cnts:
                    area = cv2.contourArea(c) # 计算轮廓面积
                    if area < 1800 or area > 20000: # 面积筛选
                        continue
                    
                    M0_red = cv2.moments(c)
                    if M0_red["m00"] != 0: # 确保避免除以零错误
                        car_video_location = [int(M0_red["m10"] / M0_red["m00"]), int(M0_red["m01"] / M0_red["m00"])]
                        ptLeftTop = (car_video_location[0] - h, car_video_location[1] - w)
                        ptRightBottom = (car_video_location[0] + h, car_video_location[1] + w)
                        point_color = color[3]
                        thickness = 2 
                        lineType = 4
                        
                        cv2.rectangle(image, ptLeftTop, ptRightBottom, point_color, thickness, lineType)
                        cv2.putText(image, color[2], (car_video_location[0]-20, car_video_location[1]-30),
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, color[3], 2)
                        # 更新结果信息
                        self.resultInfo = f"颜色: {color[2]}"
                        
                        # 存放检测结果和中心点坐标
                        center_point = (int(car_video_location[0]), int(car_video_location[1]))
                        result = []
                        result.append(color[2])
                        result.append(center_point)
                        self.result_message.append(result)
                        
                        print(f"self.resultInfo: {self.resultInfo}")
                        print(f"self.result_message: {self.result_message}")
        
                        # 添加参数信息
                        model_info = f" {color[2]} 匹配 \n HSV Min: {color[0]} \n HSV Max: {color[1]}\n"
                        self.add_parameter_info(model_info)
                
        cv2.imwrite(res_file_path, image)
        
        return image

    def pcb_test(self, image):
        try:
            def claafication(num):
                try:
                    if num == 0:
                        return 0
                    elif num == 1 or num == 2:
                        return 1
                    elif num == 3 or num == 4:
                        return 2
                    elif num >= 5:
                        return 3
                except Exception as e:
                    print(f"Error in claafication: {e}")
                    return 0
                
            # 清空之前检测的信息
            self.result_message = []
            
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            gaussian = cv2.GaussianBlur(gray, (3, 3), 0)
            circles1 = cv2.HoughCircles(gaussian, cv2.HOUGH_GRADIENT, 1, 30, param1=200, param2=25, minRadius=10, maxRadius=14)
            cir_point={}
            if  circles1 is not None:
                circles = np.round(circles1[0, :])
                circles = np.uint16(np.around(circles))
                circles_num = len(circles)
                
                i = 0
                for [x, y, r] in circles:
                    i = i+1
                    cv2.circle(image, (x, y), r, (0, 255, 0), 2)  # 画圆
                    cv2.circle(image, (x, y), 2, (0, 255, 0), 2)  # 标出中心点
                    cv2.putText(img=image,text=f"{i}",org=(x,y),fontFace=cv2.FONT_HERSHEY_COMPLEX,
                                fontScale=0.5,color=(255,255,0),thickness=2) # 字体尺寸、颜色和字体的粗细
                    cir_point[i] = [x,y]

                print("缺陷个数为%s" %circles_num)
                
                pcb_txt = str(circles_num)
                self.resultInfo = f"缺陷：{pcb_txt}"
                                
                class_num = claafication(circles_num)
                result = []
                result.append(int(class_num))
                result.append(int(circles_num))
                self.result_message.append(result)
                
                print(f"self.resultInfo: {self.resultInfo}")
                print(f"self.result_message: {self.result_message}")
                
                self.save_img(image)
                return image
            else:
                print("缺陷个数为0")
                self.resultInfo = f"缺陷：{str(0)}"
                
                result = []
                result.append(int(0))
                result.append(int(0))
                self.result_message.append(result)
                self.save_img(image)
                return image
        except Exception as e:
            print(f"Error in pcb_test: {e}")
            return image

    def run(self):
        try:
            current_dir = os.path.dirname(os.path.abspath(__file__))
            # self.cap = cv2.VideoCapture(0)
            # self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
            ori_file_path = os.path.join(current_dir, "..", "img/ori", "frame.png")

            while not self.Is_exit:
                if not self.stop:
                    # ret, frame = self.cap.read()
                    self.frame = self.virtualCam.get_image()
                    frame = self.frame.copy()
                    if self.frame is not None:
                        image = frame.copy()
                        cv2.imwrite(ori_file_path, image)
                        ### 将原始图像放入原始图像队列
                        try:
                            if not self.raw_frame_queue.full():
                                self.raw_frame_queue.put(frame)
                            else:
                                self.raw_frame_queue.get_nowait()  
                                self.raw_frame_queue.put(frame) 
                        except queue.Empty:
                            pass

                        ### (自动模式)如果启用了二维码检测，则处理图像并放入处理图像队列
                        if self.detect_qr_code != 0:
                            if self.detect_qr_code == 1:
                                processed_image = self.QR_test(image)
                            elif self.detect_qr_code == 2:
                                processed_image = self.OCR_test(image)
                            elif self.detect_qr_code == 3:
                                processed_image = self.shape_test(image)
                            elif self.detect_qr_code == 4:
                                processed_image = self.color_test(image)
                            elif self.detect_qr_code == 5:
                                processed_image = self.pcb_test(image)
                            try:
                                if not self.raw_frame_queue.full():
                                    self.raw_frame_queue.put(processed_image)
                                else:
                                    self.raw_frame_queue.get_nowait() 
                                    self.raw_frame_queue.put(processed_image) 
                            except queue.Empty:
                                pass

                else:
                    ### (手动模式) 如果启用了二维码检测，则处理图像并放入处理图像队列，处理img/ori下保存的图像
                    ### 下面手动选择视觉检测方法

                    if self.detect_qr_code != 0:
                        image = cv2.imread(ori_file_path)
                        if self.detect_qr_code == 1:
                            processed_image = self.QR_test(image)
                        elif self.detect_qr_code == 2:
                            processed_image = self.OCR_test(image)
                        elif self.detect_qr_code == 3:
                            processed_image = self.shape_test(image)
                        elif self.detect_qr_code == 4:
                            processed_image = self.color_test(image)
                        elif self.detect_qr_code == 5:
                            processed_image = self.pcb_test(image)
                        
                        try:
                            if not self.raw_frame_queue.full():
                                self.raw_frame_queue.put(processed_image)
                            else:
                                self.raw_frame_queue.get_nowait() 
                                self.raw_frame_queue.put(processed_image) 
                        except queue.Empty:
                            pass

                        self.stop = True
                        self.detect_qr_code = 0
                    time.sleep(0.1)
                    

        except Exception as e:
            print(f"Error in run: {e}")



class IOACamera():

    def __init__(self, server_ip = "127.0.0.1", server_port=8527, head_ver=0x02, device_id = 1, camera_id = 1):
        try:
            self.socket_lock = threading.Lock()
            self.image_lock = threading.Lock()
            self.weather_lock = threading.Lock()
            #self.total_data = bytes()
            #self.recv_data = bytes()
            self.server_ip = server_ip
            self.server_port = server_port
            self.HEADER_VER = head_ver
            self.device_id = device_id
            self.camera_id = camera_id
            self.video_image = {}
            self.start_camera_data = {
                "cmd": "getImg",
                "deviceType": "\u89c6\u89c9\u76f8\u673a", 
                "deviceId": self.device_id,
                "cameraNum": self.camera_id
                }
            self.stop_camera_data = {
                "cmd": "stop"
            }

            ## 创建 socket 对象
            self.conn = socket.socket()         
            self.conn.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 5000)
            self.conn.connect((self.server_ip, self.server_port))
            # self.conn.setblocking(False)
            threading.Thread(target=self.recv_socket_data, args=()).start()
        except Exception as e:
            print(e)
            exit(0)
    
    def op_camera(self, data):
        send_data = json.dumps(data)
        #print(send_data)
        self.socket_lock.acquire()
        recv = self.pack_data_frame(send_data)
        self.send_socket_data(recv)
        self.socket_lock.release()
        return 0

    # send socket数据
    def send_socket_data(self, data):
        try:
            self.conn.send(data)
        except Exception as e:
            print(e)
    
    # 接受来自socket服务端的数据，并解析
    # socket服务端大约400ms返回一次数据
    def recv_socket_data(self):
        if 'debian' in platform.platform():
            while True:
                try:
                    total_data = bytes()
                    while True:
                        recv_data = self.conn.recv(1460)
                        total_data += recv_data
                        if(len(recv_data) != 1460):
                            break
                    parsed_data = self.unpack_data_frame(total_data)
                    if type(parsed_data) == type("a"):
                        parsed_data = json.loads(parsed_data)

                        if "humi" in parsed_data.keys():
                            self.weather_lock.acquire()
                            self.weather = parsed_data
                            self.weather_lock.release()
                        elif "cameraImg" in parsed_data.keys():
                            self.image_lock.acquire()
                            self.video_image = parsed_data
                            self.image_lock.release()
                    time.sleep(0.4)
                except Exception as e:
                    print(e)
        else:

            while True:
                try:                
                    #print("{} recv data".format(int(round(time.time() * 1000))))
                    recv_data = self.conn.recv(1 << 20)
                    parsed_data = self.unpack_data_frame(recv_data)
                    if type(parsed_data) == type("a"):
                        parsed_data = json.loads(parsed_data)

                        if "humi" in parsed_data.keys():
                            self.weather_lock.acquire()
                            self.weather = parsed_data
                            self.weather_lock.release()
                        elif "cameraImg" in parsed_data.keys():
                            self.image_lock.acquire()
                            self.video_image = parsed_data
                            self.image_lock.release()
                    time.sleep(0.4)
                except Exception as e:
                    print(e)

    # 打包data数据为bytes帧
    # data：json命令报文部分
    def pack_data_frame(self, data):
        cs = 0x00
        buf = bytes(data, encoding='utf-8')
        size = len(buf)+10
        head_tag = 0xaaff
        head_ver = self.HEADER_VER
        head_len = size-4
        tail_tag = 0x55ff
        buffer = b''
        tmp = struct.pack('<HBI', head_tag, head_ver, head_len)
        buffer += tmp
        buffer += buf
        cs = self.checksum(buffer, size)
        tmp = struct.pack('<BH', cs, tail_tag)
        buffer += tmp
        return buffer

    # 从buffer中解包
    # 异常返回0
    # 正常返回长度大于0的字符串
    def unpack_data_frame(self, buffer):
        length = len(buffer)
        size = 0
        if length < 10:
            print("length <10 return")
            return 0
        # <代表小端
        # H代表unsigned short
        # B代表unsigned char
        # I代表unsigner int
        # 格式化解析buffer[0-7]
        head_tag, head_ver, head_len = struct.unpack('<HBI', buffer[0:7])
        if (head_tag == 0xaaff):
            size = head_len+4
            if length < size:
                print("length {} < size {} return".format(length, size))
                return 0
            cs, tail_tag = struct.unpack('<BH', buffer[size-3:size])
            if ((tail_tag == 0x55ff) and (cs == self.checksum(buffer, size))):
                # print(size)
                data = str(buffer[7:size-3], encoding='utf-8')
                return data
            else:
                return 0
        else:
            return 0
    
    # 计算校验和
    # 校验和规则为：命令类型+帧长度+数据
    # buffer：[x,x,x,x]格式
    # size：有效数据的长度
    def checksum(self, buffer, size):
        cs = 0
        i = 2
        j = size-3
        while i < j:
            cs += buffer[i]
            i += 1
        return cs & 0xff

    # 关闭socket
    def close_socket(self):
        self.conn.close()

    def fetch_image(self):
        image = {}
        self.image_lock.acquire()
        image = self.video_image
        self.image_lock.release()
        return image

    def get_image(self):
        while True:
            try:
                self.op_camera(self.start_camera_data)
                img_b64decode = self.fetch_image()['cameraImg']
                img_data = base64.b64decode(img_b64decode)
                img_array = np.fromstring(img_data, np.uint8)
                img = cv2.imdecode(img_array, cv2.COLOR_BGR2RGB)
                self.frame = img
                break
            except Exception as e:
                pass
        return img

    def show(self):
        cv2.imshow('IOA', self.frame)
        cv2.waitKey(1)

    def close(self):
        cv2.destroyAllWindows()

